package com.company.bookstore.common.jwt;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.UUID;

/**
 * JwtUtil
 * jwt 工具类
 * @author 吴经纶
 * @date 2020-08-27 15:34
 * @blame Android Team
 * 参考文献： https://my.oschina.net/xiaominmin/blog/2999894
 * https://github.com/jwtk/jjwt#jws-read-key-resolver
 */
@Slf4j
public class JwtUtil  {
    /**
     * VALID
     * NOT_VALID
     * */

//    public static SecretKey SECRETKEY=  Keys.secretKeyFor(SignatureAlgorithm.HS256);
    public static SecretKey SECRETKEY=  Keys.hmacShaKeyFor("wujinglunshishijieshangzuishuaideren".getBytes());

    public  enum JWT_STATUS  {
        VALID , NOT_VALID
    };



    /**
     * 获取Jwt 中 的username
     * */
   public static TokenMessage getTokenMessage(String jws) throws JsonProcessingException {
        Claims claims = parseToken(jws);
        String str = claims.getSubject();
        assert str!=null;
       return JSONObject.parseObject(str,TokenMessage.class);
   }


    /**
     *   setId ，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
     *   setAudience 接受者
     *   setClaims   自定义属性
     *   setSubject 代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志 而且不能改动
     *   setIssuer  签发者
     *   setIssuedAt 签发时间
     *   setExpiration(end)    过期时间
     *   signWith(signatureAlgorithm, secretKey)  签名算法以及密匙
     * */




    public static String generateToken(TokenMessage tokenMessage) throws NullPointerException,
            ExpiredJwtException, UnsupportedJwtException, IllegalArgumentException {
        if (tokenMessage.getUsername()==null){
            throw new NullPointerException("用户名为空");
        }
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        long ttlMillis = nowMillis + Constant.JWT_TTL;
        Date ttl = new Date(ttlMillis);
        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .setAudience(Constant.JWT_AUDIENCE)
                .setClaims(null)
                .setSubject(JSONObject.toJSONString(tokenMessage))
                .setIssuer(Constant.JWT_ISSUER)
                .setIssuedAt(now)
                .setExpiration(ttl)
                .signWith(SECRETKEY).compact();
    }
    /**
     * 只延长了过期时间和死亡时间 , 保证死亡时间与redis上的存活时间大致相同
     * */
    public static String refreshToken(ExpiredJwtException e) throws NullPointerException,
            ExpiredJwtException, UnsupportedJwtException, IllegalArgumentException {
        Claims claims =  e.getClaims();
        long nowMillis = System.currentTimeMillis();
        long ttlMillis = nowMillis + Constant.JWT_TTL;
        Date ttl = new Date(ttlMillis);
        return Jwts.builder()
                .setId(claims.getId())
                .setAudience(Constant.JWT_AUDIENCE)
                .setClaims(null)
                .setSubject(claims.getSubject())
                .setIssuer(Constant.JWT_ISSUER)
                .setIssuedAt(claims.getIssuedAt())
                .setExpiration(ttl)
                .signWith(SECRETKEY,SignatureAlgorithm.HS256)
                .compact();
    }
    /**
     * 解密jwt
     *  Jwts.parser()  得到DefaultJwtParser
     *   .setSigningKey(key)                 //设置签名的秘钥
     *   .parseClaimsJws(jwt).getBody();     //设置需要解析的jwt
     */
    public static Claims parseToken(String jws) {
        JwtParser  jwtParser= Jwts.parserBuilder().setSigningKey(SECRETKEY).build();
        return jwtParser
                .parseClaimsJws(jws).getBody();
    }


    public static class Constant {

        /**
         * 加密密文
         *  JWT_TTL 过期时间为一小时
         *  JWT_ALLOW_REFRESH 死亡时间是一天
         */
        public static final String JWT_AUDIENCE = "ALL";
        public static final String JWT_ISSUER = "ISSUER";
        public static final int JWT_TTL = 60*60*1000*12;
        public static final int JWT_ALLOW_REFRESH = 60*60*1000*12;
//        public static final int JWT_TTL = 2000;
//        public static final int JWT_ALLOW_REFRESH = 5000;
    }
    @Builder
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class TokenMessage{
        private String username;
//        private boolean isAdmin;
        private String role;
    }
}
